/*
* Copyright (c) 2009 by Thomas Busey and Ruj Akavipat
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the Experteyes nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Thomas Busey and Ruj Akavipat ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Thomas Busey and Ruj Akavipat BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * FindCornerTrainingSetup.java
 *
 * Created on Dec 9, 2008, 7:15:54 PM
 */
package buseylab.findcorner.gwtgridsetup;

import buseylab.gwtgrid.GWTGrid;
import buseylab.gwtgrid.ImageUtils;
import buseylab.util.PictureFilenameFilter;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

/**
 *
 * @author SQ
 */
public class FindCornerTrainingSetup extends javax.swing.JFrame {

    private ZoomJPanel zoomPanel;
    private double scale;
    private int oldCurrentFrame = -1;

    public enum TerminationCause {

        /** The user save the training info */
        COMPLETED,
        /** Error occurred */
        ERROR,
        /** The user canceled the operation */
        CANCEL
    }
    private TerminationCause terminationCause = TerminationCause.CANCEL;
    File outputFile = null;
    private File[] smallSceneFiles;
    private File[] largeSceneFiles;
    // enumeration of corners as indices into corners array
    final static int TL = 0;
    final static int TR = 1;
    final static int BR = 2;
    final static int BL = 3;
    Point[] corners = {null, null, null, null};
    Point[] smallCorners = {null, null, null, null};

    // Default values for GWTgrid setup
    double sigma = 1.0 * Math.PI;
    int numOrientations = 8;
    int numScales = 8;
    int size = 256;

    /**
     * @return the terminationCause
     */
    public TerminationCause getTerminationCause() {
        return terminationCause;
    }

    public void setGWTNumOrientations(int numOrientations) {
        this.numOrientations = numOrientations;
    }

    public void setGWTNumScales(int numScales) {
        this.numScales = numScales;
    }

    public void setGWTSigma(double sigma) {
        this.sigma = sigma;
    }

    public void setGWTSize(int size) {
        this.size = size;
    }

    /** 
     * Creates new form FindCornerTrainingSetup
     * The program will ask for output file when the user select done.
     */
    public FindCornerTrainingSetup(File[] smallSceneFiles, File[] largeSceneFiles) {
        initComponents();
        initParameters(smallSceneFiles, largeSceneFiles, null);
    }

    /**
     * Creates new form FindCornerTrainingSetup
     */
    public FindCornerTrainingSetup(File[] smallSceneFiles, File[] largeSceneFiles,
            File outputFile) {
        initComponents();
        initParameters(smallSceneFiles, largeSceneFiles, outputFile);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        jScrollPane1 = new javax.swing.JScrollPane();
        markableJLabel = new buseylab.findcorner.gwtgridsetup.MarkableJLabel();
        jPanel4 = new javax.swing.JPanel();
        jPanel5 = new javax.swing.JPanel();
        topleftRadioButton = new javax.swing.JRadioButton();
        topRightRadioButton = new javax.swing.JRadioButton();
        bottomLeftRadioButton = new javax.swing.JRadioButton();
        bottomRightRadioButton = new javax.swing.JRadioButton();
        jPanel6 = new javax.swing.JPanel();
        markButton = new javax.swing.JButton();
        doneButton = new javax.swing.JButton();
        cancleButton = new javax.swing.JButton();
        frameScrollingJPanel = new buseylab.findcorner.gwtgridsetup.FrameScrollingJPanel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        markableJLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        markableJLabel.setText("N/A");
        markableJLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jScrollPane1.setViewportView(markableJLabel);

        jPanel5.setLayout(new java.awt.GridLayout(2, 2));

        buttonGroup1.add(topleftRadioButton);
        topleftRadioButton.setSelected(true);
        topleftRadioButton.setText("Top Left");
        jPanel5.add(topleftRadioButton);

        buttonGroup1.add(topRightRadioButton);
        topRightRadioButton.setText("Top Right");
        jPanel5.add(topRightRadioButton);

        buttonGroup1.add(bottomLeftRadioButton);
        bottomLeftRadioButton.setText("Bottom Left");
        jPanel5.add(bottomLeftRadioButton);

        buttonGroup1.add(bottomRightRadioButton);
        bottomRightRadioButton.setText("Bottom Right");
        jPanel5.add(bottomRightRadioButton);

        jPanel6.setLayout(new java.awt.GridBagLayout());

        markButton.setText("Mark A Corner");
        markButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                markButtonActionPerformed(evt);
            }
        });
        jPanel6.add(markButton, new java.awt.GridBagConstraints());

        doneButton.setText("Done");
        doneButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                doneButtonActionPerformed(evt);
            }
        });
        jPanel6.add(doneButton, new java.awt.GridBagConstraints());

        cancleButton.setText("Cancel");
        cancleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancleButtonActionPerformed(evt);
            }
        });
        jPanel6.add(cancleButton, new java.awt.GridBagConstraints());

        frameScrollingJPanel.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                frameScrollingJPanelPropertyChange(evt);
            }
        });

        org.jdesktop.layout.GroupLayout jPanel4Layout = new org.jdesktop.layout.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel4Layout.createSequentialGroup()
                .add(jPanel5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 256, Short.MAX_VALUE)
                .add(jPanel6, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
            .add(frameScrollingJPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 781, Short.MAX_VALUE)
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel4Layout.createSequentialGroup()
                .add(frameScrollingJPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(jPanel5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jPanel6, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 46, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
        );

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 781, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 199, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void initParameters(File[] smallSceneFiles, File[] largeSceneFiles, File outputFile) {
        this.smallSceneFiles = smallSceneFiles;
        this.largeSceneFiles = largeSceneFiles;
        this.outputFile = outputFile;

        // Find scale assume that there is no clip in frame
        BufferedImage smallImage = ImageUtils.loadImage(smallSceneFiles[0]);
        BufferedImage largeImage = ImageUtils.loadImage(largeSceneFiles[0]);
        if (smallImage == null || largeImage == null) {
            this.scale = 1d;
        } else {
            this.scale = (double) smallImage.getWidth() / (double) largeImage.getWidth();
        }

        // Create zoom panel
        this.zoomPanel = new ZoomJPanel();
        this.zoomPanel.addDoneButtonActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                doneZoomingActionPerformed(e);
            }
        });


        this.frameScrollingJPanel.setTotalFrame(smallSceneFiles.length);
        changeFrame();
    }

    private void markButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_markButtonActionPerformed
        getCornerMarkingFromUser();
    }//GEN-LAST:event_markButtonActionPerformed

    private void doneButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_doneButtonActionPerformed
        // Check corners hints
        boolean missing = false;
        for (int i = 0; i < corners.length; i++) {
            missing = missing || corners[i] == null;
        }
        if (missing) {
            JOptionPane.showMessageDialog(this,
                    "Some corners are still missing their hints.\n" +
                    "Please gives all hints", "Missing Hints",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }

        // Save gwtgrid information
        saveGWTGrids();

        //
        this.dispose();
}//GEN-LAST:event_doneButtonActionPerformed

    private void frameScrollingJPanel1PropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_frameScrollingJPanel1PropertyChange
        changeFrame();
    }//GEN-LAST:event_frameScrollingJPanel1PropertyChange

    private void cancleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancleButtonActionPerformed
        this.dispose();
    }//GEN-LAST:event_cancleButtonActionPerformed

    private void frameScrollingJPanelPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_frameScrollingJPanelPropertyChange
        changeFrame();
    }//GEN-LAST:event_frameScrollingJPanelPropertyChange

    private void changeFrame() {
        int currentFrame = this.frameScrollingJPanel.getCurrentFrame();
        if (this.oldCurrentFrame != currentFrame) {
            if (this.smallSceneFiles != null) {
                BufferedImage image = ImageUtils.loadImage(smallSceneFiles[currentFrame - 1]);
                if (image != null) {
                    this.markableJLabel.setIcon(new ImageIcon(image));
                    this.markableJLabel.setText(null);
                    this.oldCurrentFrame = currentFrame;
                } else {
                    this.markableJLabel.setIcon(null);
                    this.markableJLabel.setText("N/A");
                }
            }

            // Clear current corner
            for (int i = 0; i < this.corners.length; i++) {
                this.corners[i] = null;
                this.smallCorners[i] = null;
            }
            // Set proper display
            this.markableJLabel.setCorners(
                    this.smallCorners[TL], this.smallCorners[TR],
                    this.smallCorners[BL], this.smallCorners[BR],
                    MarkableJLabel.MarkColor.GREEN);
            this.markableJLabel.repaint();
        }
    }

    private void doneZoomingActionPerformed(ActionEvent e) {
        Point2D selectedPoint = this.zoomPanel.getSelectedPoint();
        if (selectedPoint != null) {
            // Get a point from zoom panel
            Point p = new Point();
            p.setLocation(selectedPoint);
            Point smallPoint = new Point((int) (p.x * this.scale), (int) (p.y * this.scale));
            // Add point to a proper place
            if (this.topRightRadioButton.isSelected()) {
                this.corners[TR] = p;
                this.smallCorners[TR] = smallPoint;
            } else if (this.topleftRadioButton.isSelected()) {
                this.corners[TL] = p;
                this.smallCorners[TL] = smallPoint;
            } else if (this.bottomLeftRadioButton.isSelected()) {
                this.corners[BL] = p;
                this.smallCorners[BL] = smallPoint;
            } else if (this.bottomRightRadioButton.isSelected()) {
                this.corners[BR] = p;
                this.smallCorners[BR] = smallPoint;
            }
            // Set proper display
            this.markableJLabel.setCorners(
                    this.smallCorners[TL], this.smallCorners[TR],
                    this.smallCorners[BL], this.smallCorners[BR],
                    MarkableJLabel.MarkColor.GREEN);
            this.markableJLabel.repaint();
        }
        this.setEnabled(true);
    }

    private void getCornerMarkingFromUser() throws HeadlessException {
        // Disable input
        this.setEnabled(false);

        // Get previous point
        Point p = null;
        if (this.topRightRadioButton.isSelected()) {
            p = this.corners[TR];
        } else if (this.topleftRadioButton.isSelected()) {
            p = this.corners[TL];
        } else if (this.bottomLeftRadioButton.isSelected()) {
            p = this.corners[BL];
        } else if (this.bottomRightRadioButton.isSelected()) {
            p = this.corners[BR];
        }

        int frameNumber = this.frameScrollingJPanel.getCurrentFrame();
        this.zoomPanel.setImage(ImageUtils.loadImage(largeSceneFiles[frameNumber - 1]));
        this.zoomPanel.setMouseSelectedPoint(p);

        // Open a zoom able panel and get the marking
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.add(this.zoomPanel);
        final ActionListener listener = new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                // Remove ourselve from listener
                zoomPanel.removeDoneButtonActionListener(this);
                // Enable for input
                setEnabled(true);
                // Dispose frame
                frame.dispose();
            }
        };
        this.zoomPanel.addDoneButtonActionListener(listener);
        frame.setSize(512, 512);
        frame.setVisible(true);
    }

    public void saveGWTGrids() {
        try {

            double[][][] freqKernels = GWTGrid.genFreqKernel(
                    size, numScales, numOrientations, sigma);
            // translate the corners
            Point[] translatedClicks = new Point[4];

            // Get current scene
            BufferedImage scene = null;
            int currentFrame = this.frameScrollingJPanel.getCurrentFrame() - 1;
            if (this.largeSceneFiles != null) {
                scene = ImageUtils.loadImage(largeSceneFiles[currentFrame]);
            }

            // Sanity check
            if (scene == null) {
                JOptionPane.showMessageDialog(this, "Training scene is missing", "File Not Found", JOptionPane.ERROR_MESSAGE);
                this.terminationCause = TerminationCause.ERROR;
                return;
            }


            FileOutputStream fos = new FileOutputStream(this.outputFile);
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            for (int corner = 0; corner < 4; corner++) {
                // get image subsection
                double[] pixels = ImageUtils.RGBtoGrayDouble(ImageUtils.getPixels(
                        scene, corners[corner].x - (int) (size / 2.0),
                        corners[corner].y - (int) (size / 2.0), size, size));
                // translate xClick, yClick these into 128 x 128 space
                int xClickTranslated = (int) (size / 2.0);
                int yClickTranslated = (int) (size / 2.0);
                translatedClicks[corner] = new Point(xClickTranslated, yClickTranslated);
                GWTGrid gwtgrids = new GWTGrid(pixels, size, freqKernels);
                oos.writeObject(gwtgrids.getMagnitudeResp(
                        translatedClicks[corner].x, translatedClicks[corner].y));
                oos.writeObject(gwtgrids.getPhaseResp(
                        translatedClicks[corner].x, translatedClicks[corner].y));

            }

            // write out the encoded file name
            oos.writeObject(new String(largeSceneFiles[currentFrame].getName()));

            oos.close();
            fos.close();

            this.terminationCause = TerminationCause.COMPLETED;
        } catch (Exception e) {
            e.printStackTrace();
            this.terminationCause = TerminationCause.ERROR;
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            File[] scenes = new File("LargeCleanedScene/").listFiles(new PictureFilenameFilter());

            public void run() {
                new FindCornerTrainingSetup(
                        scenes, scenes, new File("gwtgrids.dat")).setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JRadioButton bottomLeftRadioButton;
    private javax.swing.JRadioButton bottomRightRadioButton;
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JButton cancleButton;
    private javax.swing.JButton doneButton;
    private buseylab.findcorner.gwtgridsetup.FrameScrollingJPanel frameScrollingJPanel;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JPanel jPanel6;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton markButton;
    private buseylab.findcorner.gwtgridsetup.MarkableJLabel markableJLabel;
    private javax.swing.JRadioButton topRightRadioButton;
    private javax.swing.JRadioButton topleftRadioButton;
    // End of variables declaration//GEN-END:variables
}
